# @Time    : 2024/2/14 23:23
# @Author  : 🍁
# @File    : file.py
# @Software: PyCharm
import json
import requests

from django.shortcuts import render, redirect, reverse
from django.views import View
from django import http
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings

from web import models
from web.forms.file import FileForms, FileModel
from web.utils.cos import cos

from sts.sts import Sts


# 文件主页
class FileView(View):
    def get(self, request, project_id):
        folder_id = request.GET.get("folder", "")
        # 导航条的设计
        url_list = []
        # 文件列表
        file_dict = {"file": [], "folder": []}
        if folder_id.isdecimal():  # 判断传的参数是否是数字
            # 当前项目的文件夹，用来写导航条
            file_object = models.File.objects.filter(id=folder_id, type=2, project_id=project_id).first()
            # 当前项目的文件夹
            row_object = file_object
            # 死循环一个文件对象,用来写导航条
            while row_object:
                # 把当前文件对象添加到列表中的首位
                url_list.insert(0, row_object)
                # 拿到当前文件对象的父目录,重新赋值给文件对象
                row_object = row_object.parent_id
            # 当前文件夹下的所有文件，用来写文件列表
            file_object = models.File.objects.filter(parent_id=folder_id, project_id=project_id)

        else:
            # 获取没有父目录的所有文件
            file_object = models.File.objects.filter(project_id=project_id, parent_id__isnull=True)
        form = FileForms(request, file_object)
        # 文件
        try:
            for item in file_object:
                if item.type == 1:
                    # 类型是文件的
                    file_dict["file"].append({"id": item.id, "item": item, "size": round(int(item.size) / 1024, 2)})
                else:
                    # 类型是文件夹的
                    file_dict["folder"].append({"id": item.id, "item": item})
        except TypeError:
            file_dict = {"file": [], "folder": []}

        return render(request, "file.html",
                      {"form": form, "url_list": url_list, "file_dict": file_dict, "folder": folder_id})

    def post(self, request, project_id):
        # 获取路由中的参数
        file_object = None
        folder_id = request.GET.get("folder", "")
        if folder_id.isdecimal():
            file_object = models.File.objects.filter(id=folder_id, type=2, project_id=project_id).first()

        fid = request.POST.get("fid", "")
        edit_object = None
        if fid.isdecimal():
            # 如果有fid 就是编辑
            edit_object = models.File.objects.filter(id=fid, type=2, project_id=project_id).first()
        # 判断是编辑文件还是新建文件
        if edit_object:
            # 编辑文件
            form = FileForms(request, file_object, data=request.POST, instance=edit_object)
        else:
            # 新建文件
            form = FileForms(request, file_object, data=request.POST)
        folder_object = file_object
        if form.is_valid():
            # 添加项目id
            form.instance.project_id = request.tartec.project
            # 添加文件类型
            form.instance.type = 2
            # 添加更新者
            form.instance.revise = request.tartec.user
            # 父目录
            form.instance.parent_id = folder_object
            form.save()
            return http.JsonResponse({"status": True})
        return http.JsonResponse({"status": False, "error": form.errors})


# 删除文件/文件夹
class DelFile(View):
    def get(self, request, project_id):
        fid = request.GET.get("fid")
        delete_object = models.File.objects.filter(id=fid, project_id=project_id).first()
        if delete_object.type == 1:
            # 如果类型是文件(数据库删除文件，cos删除文件，归还已使用的空间)
            request.tartec.project.space_used -= int(delete_object.size)
            # 保存一下数据
            request.tartec.project.save()
            # 数据库删除文件
            delete_object.delete()
            # cos删除文件
            cos.delete_file(request.tartec.project.bucket, request.tartec.project.region,
                            delete_object.key)
        else:
            # 如果是文件夹(数据库删除文件夹，删除文件夹下的所有文件，归还已使用的空间)
            # 总使用空间
            all_space_used = 0
            # 文件列表
            file_list = []
            # 文件夹对象列表
            folder_list = [delete_object, ]
            for folder in folder_list:
                # 当前文件夹下的所有文件夹对象
                folder_all = models.File.objects.filter(project_id=request.tartec.project, parent_id=folder).order_by(
                    "-type")
                for children in folder_all:  # 循环文件夹下的文件/文件夹
                    if children.type == 1:
                        # 如果是文件
                        # 添加到文件列表
                        file_list.append(children.key)
                        all_space_used += int(children.size)
                    else:
                        # 如果是文件夹
                        # 添加到文件夹列表，用来循环
                        folder_list.append(children)
            # 批量cos 删除
            if file_list:
                cos.delete_file(request.tartec.project.bucket, request.tartec.project.region,
                                file_list)
            # 减去该文件夹下的所有文件的大小
            if all_space_used:
                request.tartec.project.space_used -= all_space_used
                request.tartec.project.save()
            delete_object.delete()

        return http.JsonResponse({"status": True})


# 获取临时凭证
class CosCredential(View):
    # 免去csrf 验证
    @csrf_exempt
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def post(self, request, project_id):
        # 获取上传文件的大小
        file_size = json.loads(request.body)
        # 获取用户当前的价格策略
        classify = request.tartec.price_classify
        all_size = 0
        # 判断上传文件是否大于价格策略
        for item in file_size:
            # item["size"] = B
            all_size += item["size"]
            if item["size"] > classify.project_file_space * 1024 * 1024:
                return http.JsonResponse(
                    {"status": False, "error": "单个文件超出限制(最大{}M, 文件：{})，请升级套餐".format(
                        classify.project_file_space, item["name"])})
        if all_size > (classify.every_project_space * 1024 * 1024 * 1024 - request.tartec.project.space_used):
            return http.JsonResponse({"status": False, "error": "总文件大小超出限制(最大{}G)，请升级套餐".format(
                classify.project_file_space)})

        result_dict = cos.credential(request.tartec.project.bucket, request.tartec.project.region)

        return http.JsonResponse({"status": True, "data": result_dict})


# 获取前端传过来的文件 上传到数据库
class UploadFile(View):
    @csrf_exempt
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def post(self, request, project_id):
        form = FileModel(request, data=request.POST)
        if form.is_valid():
            file_dict = form.cleaned_data
            file_dict.pop("etag")
            file_dict.update({"type": 1, "revise": request.tartec.user, "project_id": request.tartec.project})
            file_model = models.File.objects.create(**file_dict)
            data = {
                "id": file_model.id,
                "title": file_model.title,
                "file_size": round(int(file_model.size) / 1024, 2),
                "username": file_model.revise.username,
                "update": file_model.update_date.strftime("%Y年%m月%d日%H:%M"),
                "download_url": reverse("download_file", kwargs={"project_id": project_id, "file_id": file_model.id})
            }
            # 更新项目使用空间
            request.tartec.project.space_used += int(data["file_size"])
            request.tartec.project.save()
            return http.JsonResponse({"status": True, "data": data})
        return http.JsonResponse({"status": False, "error": form.errors})


# 下载文件
class DownloadFileView(View):
    def get(self, request, project_id, file_id):
        file_object = models.File.objects.filter(id=file_id, project_id=project_id, type=1).first()
        data = requests.get(file_object.file_path)
        res = data.content
        response = http.HttpResponse(res)
        # 添加响应头
        # response['Content-Disposition'] = "attachment; filename={}" 响应头这样设置直接就是下载
        response['Content-Disposition'] = "attachment; filename={}".format(file_object.title)
        return response
